﻿using SharpSoft.Data.GSQL;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace SharpSoft.Data.DAOS
{
    public static class DAOCreator
    {
        static DAOCreator()
        {

        }

        public static string Generate(string schemaCreate, string name)
        {
            var sb = new StringBuilder();
            sb.AppendLine($"{spc(1)}public class {name}DAL:SharpSoft.Data.DAL.DAL_Base");
            sb.AppendLine(spc(1) + "{");
            sb.AppendLine(spc(2) + $"public {name}DAL(Database p_db, int p_userid) : base(p_db, p_userid) {{ }}");
            sb.AppendLine(spc(2) + $"public override void CreateSchema(string schemaFile = \"\"){{base.CreateSchema(\"{name.ToLower()}.txt\");}}");
            GSQLAnalyzer GLA = new GSQLAnalyzer(schemaCreate);
            var stams = GLA.ReadStatements();
            if (stams != null)
            {
                foreach (var item in stams)
                {
                    if (item is CreateTableStatement cts)
                    {
                        var method = CreateInsertMethod(cts);
                        sb.AppendLine(method);
                        sb.AppendLine();
                        method = CreateUpdateMethod(cts);
                        sb.AppendLine(method);
                        sb.AppendLine();
                        method = CreateDeleteMethod(cts);
                        sb.AppendLine(method);
                        sb.AppendLine();
                        method = CreateSelectMethod(cts);
                        sb.AppendLine(method);
                        sb.AppendLine();
                    }
                }
            }
            sb.AppendLine(spc(1) + "}");
            sb.AppendLine();
            if (stams != null)
            {
                foreach (var item in stams)
                {
                    if (item is CreateTableStatement cts)
                    {
                        var model = CreateTableModel(cts);
                        sb.AppendLine(model);
                        sb.AppendLine();
                    }
                }
            }
            return sb.ToString();
        }

        public static string Generate(System.Reflection.Assembly _assembly, string resname, string dalname)
        {
            var txt = GetResFile(_assembly, resname);
            return Generate(txt, dalname);
        }

        private static string spc(int count = 1)
        {
            return "".PadRight(count * 4, ' ');
        }
        /// <summary>
        /// 创建数据表模型
        /// </summary>
        /// <param name="cts"></param>
        /// <returns></returns>
        public static string CreateTableModel(CreateTableStatement cts)
        {
            resolTable(cts, out string tbname, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);

            var sb = new StringBuilder();
            sb.Append($"{spc(1)}public class {tbname}Model");
            sb.AppendLine(spc(1) + "{");
            foreach (var item in colswithType)
            {
                sb.AppendLine(spc(2) + $"public {item.Value} {item.Key} {{get;set;}}");
            }
            sb.AppendLine(spc(1) + "}");
            return sb.ToString();
        }
        public static string CreateInsertMethod(CreateTableStatement cts)
        {
            resolTable(cts, out string tbname, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);
            var sql = CreateInsertSql(cts, out hasauto);
            StringBuilder sb = new StringBuilder();
            var args = getArgList(out string objstr, colswithType, pk);
            sb.AppendLine(spc(2) + $"public {(hasauto ? "int" : "void")} Insert{tbname}({args})");
            sb.AppendLine(spc(2) + "{");
            sb.AppendLine(spc(3) + $"var sql=$@\"{sql}\";");
            if (hasauto)
            {
                sb.AppendLine(spc(3) + $"return db.ExecuteInt32(sql,{objstr});");
            }
            else
            {
                sb.AppendLine(spc(3) + $"db.ExecuteNonQuery(sql,{objstr});");
            }
            sb.AppendLine(spc(2) + "}");
            return sb.ToString();
        }
        public static string CreateUpdateMethod(CreateTableStatement cts)
        {
            resolTable(cts, out string tbname, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);
            var sql = CreateUpdateSql(cts, out bool haspk);
            if (!haspk)
            {
                return "";
            }
            StringBuilder sb = new StringBuilder();
            var args = getArgList(out string objstr, colswithType);
            sb.AppendLine(spc(2) + $"public int Update{tbname}({args})");
            sb.AppendLine(spc(2) + "{");
            sb.AppendLine(spc(3) + $"var sql=$@\"{sql}\";");

            sb.AppendLine(spc(3) + $"return db.ExecuteNonQuery(sql,{objstr});");
            sb.AppendLine(spc(2) + "}");
            return sb.ToString();
        }
        public static string CreateDeleteMethod(CreateTableStatement cts)
        {
            resolTable(cts, out string tbname, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);
            var sql = CreateDeleteSql(cts, out bool haspk);

            if (!haspk)
            {
                return "";
            }
            var pkarg = $"{colswithType[pk]} {pk}";

            StringBuilder sb = new StringBuilder();

            sb.AppendLine(spc(2) + $"public int Delete{tbname}({pkarg})");
            sb.AppendLine(spc(2) + "{");
            sb.AppendLine(spc(3) + $"var sql=$@\"{sql}\";");

            sb.AppendLine(spc(3) + $"return db.ExecuteNonQuery(sql,new{{{pk}}});");
            sb.AppendLine(spc(2) + "}");
            return sb.ToString();
        }
        public static string CreateSelectMethod(CreateTableStatement cts)
        {
            resolTable(cts, out string tbname, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);
            var sql = CreateSelectSql(cts, out bool haspk);

            if (!haspk)
            {
                return "";
            }
            var pkarg = $"{colswithType[pk]} {pk}";

            StringBuilder sb = new StringBuilder();

            sb.AppendLine(spc(2) + $"public {tbname}Model Get{tbname}({pkarg})");
            sb.AppendLine(spc(2) + "{");
            sb.AppendLine(spc(3) + $"var sql=$@\"{sql}\";");

            sb.AppendLine(spc(3) + $"return db.ExecuteObject<{tbname}Model>(sql,new{{{pk}}});");
            sb.AppendLine(spc(2) + "}");
            return sb.ToString();
        }

        /// <summary>
        /// 获取参数列表
        /// </summary>
        /// <param name="objstr">输出由参数组成的对象字符串</param>
        /// <param name="colswithType"></param>
        /// <param name="exp">排除的参数</param>
        /// <returns>返回函数的参数列表字符串</returns>
        private static string getArgList(out string objstr, Dictionary<string, string> colswithType, string exp = null)
        {
            if (!string.IsNullOrEmpty(exp) && colswithType.ContainsKey(exp))
            {
                colswithType.Remove(exp);
            }
            List<string> args = new List<string>();
            List<string> objprops = new List<string>();
            foreach (var item in colswithType)
            {
                args.Add($"{item.Value} {item.Key}");
                objprops.Add(item.Key);
            }
            objstr = $"new {{{string.Join(",", objprops)}}}";
            return string.Join(",", args);
        }

        private static string CreateInsertSql(CreateTableStatement cts, out bool hasauto)
        {
            resolTable(cts, out string tbname, out hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);

            StringBuilder sb = new StringBuilder();
            sb.Append("INSERT INTO ");
            sb.Append($"`{tbname}`");
            sb.Append("(");
            var collist = new List<string>();
            var valllist = new List<string>();
            foreach (var item in cols)
            {
                collist.Add($"`{item}`");
                valllist.Add($"@{item}");
            }
            sb.Append(string.Join(",", collist));
            sb.Append(")VALUES(");
            sb.Append(string.Join(",", valllist));
            sb.Append(");");
            if (hasauto)
            {
                sb.Append("SELECT LAST_INSERT_ID();");
            }
            return sb.ToString();
        }

        private static string CreateUpdateSql(CreateTableStatement cts, out bool haspk)
        {
            resolTable(cts, out string tbname, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);
            haspk = !string.IsNullOrEmpty(pk);
            if (!haspk)
            {//无主键的不参与更新
                return null;
            }
            StringBuilder sb = new StringBuilder();
            sb.Append("UPDATE ");
            sb.Append($"`{tbname}`");
            sb.Append(" SET ");
            var collist = new List<string>();
            foreach (var item in cols)
            {
                collist.Add($"`{item}`=@{item}");
            }
            sb.Append(string.Join(",", collist));
            sb.Append($" WHERE `{pk}`=@{pk};");

            return sb.ToString();
        }
        private static string CreateDeleteSql(CreateTableStatement cts, out bool haspk)
        {
            resolTable(cts, out string tbname, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);
            haspk = !string.IsNullOrEmpty(pk);
            if (!haspk)
            {//无主键的不参与删除
                return null;
            }
            StringBuilder sb = new StringBuilder();
            sb.Append($"DELETE FROM `{tbname}`");
            sb.Append($" WHERE `{pk}`=@{pk};");

            return sb.ToString();
        }
        private static string CreateSelectSql(CreateTableStatement cts, out bool haspk)
        {
            resolTable(cts, out string tbname, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType);
            haspk = !string.IsNullOrEmpty(pk);
            if (!haspk)
            {//无主键的不参与查询
                return null;
            }
            var collist = new List<string>();

            foreach (var item in colswithType.Keys)
            {
                collist.Add($"`{item}`");
            }
            StringBuilder sb = new StringBuilder();
            sb.Append($"SELECT {string.Join(",", collist)} FROM `{tbname}`");
            sb.Append($" WHERE `{pk}`=@{pk};");

            return sb.ToString();
        }

        private static void resolTable(CreateTableStatement cts, out string tableName, out bool hasauto, out string pk, out string[] cols, out Dictionary<string, string> colswithType)
        {
            hasauto = false;
            tableName = cts.Table.Name;
            var colsList = new List<string>();
            pk = null;
            colswithType = new Dictionary<string, string>();
            if (cts.ColumnDefines != null)
            {
                foreach (var col in cts.ColumnDefines)
                {
                    if (col is ColumnDefine cd)
                    {
                        string type;
                        switch (cd.Type)
                        {
                            case DataType.Guid:
                            case DataType.Char:
                            case DataType.NChar:
                            case DataType.VarChar:
                            case DataType.NVarChar:
                            case DataType.Text:
                                type = "string";
                                break;
                            case DataType.Int:
                                type = "int" + (cd.Nullable ? "?" : "");
                                break;
                            case DataType.Long:
                                type = "long" + (cd.Nullable ? "?" : "");
                                break;
                            case DataType.Decimal:
                                type = "double" + (cd.Nullable ? "?" : "");
                                break;
                            case DataType.Boolean:
                                type = "bool" + (cd.Nullable ? "?" : "");
                                break;
                            case DataType.Binary:
                                type = "byte[]";
                                break;
                            case DataType.DateTime:
                            case DataType.Time:
                                type = "DateTime" + (cd.Nullable ? "?" : "");
                                break;
                            default:
                                type = $"[未知类型:{cd.Type.ToString()}]";
                                break;
                        }
                        colswithType.Add(cd.Name.Name, type);
                        if (cd.Autoincrement)
                        {
                            hasauto = true;
                        }
                        else
                        {
                            colsList.Add(cd.Name.Name);
                        }
                        if (cd.IsPrimaryKey)
                        {
                            pk = cd.Name.Name;
                        }
                    }
                }
            }
            cols = colsList.ToArray();
        }


        public static string GetResFile(System.Reflection.Assembly _assembly, string resname)
        {
            System.IO.Stream stream = _assembly.GetManifestResourceStream(resname);
            if (stream == null)
            {
                throw new Exception($"资源文件不存在:[{resname}]");
            }
            using (var sr = new StreamReader(stream, Encoding.UTF8))
            {
                return sr.ReadToEnd();
            }
        }


    }

}
